План урока¶

  • Зачем визуализировать?
  • Выбираем график
  • Правила хорошего тона, общепринятые практики, нюансы
  • Работа с цветом
  • Как не внести смуту
  • Как визуализировать большое количество точек
  • Что делать с выбросами
  • Библиотеки Python

Зачем визуализировать?¶

Визуализация наглядно показывает тренды, паттерны и выбросы, которые тяжело увидеть в числах, особенно когда их много

Anscombe's Quartet - работа Anscombe, F. J. (1973). “Graphs in Statistical Analysis”

Эти 4 набора данных имеют одинаковые статистические свойства, но на самом деле распределены сильно по-разному

SummaryUrl

image.png

Другой пример - Datasaurus Dozen (Justin Matejka, George Fitzmaurice, Alberto Cairo)

DinoUrl

In [1]:
%matplotlib inline
%load_ext autoreload
%autoreload 2
from IPython.core.display import display, HTML
display(HTML("<style>.container { width:95% !important; }</style>"))

Выбираем график¶

Выбор графика начинается с определения задачи: хотим ли мы

  • увидеть распределение?
  • сравнить величины?
  • увидеть части целого?
  • отследить изменения во времени?

DinoUrl

Специально для seaborn

ChartUrl

In [1]:
import pandas as pd
import numpy as np

import seaborn as sns
import matplotlib
from matplotlib import pyplot as plt
%matplotlib inline
%config InlineBackend.figure_format='retina'

import warnings
warnings.filterwarnings("ignore")

pd.__version__, np.__version__, sns.__version__, matplotlib.__version__
Out[1]:
('2.0.3', '1.24.4', '0.13.0', '3.7.3')

Гистограмма. Используем для визуализации распределений. Тип данных - непрерывные.¶

In [45]:
penguins = sns.load_dataset('penguins')
penguins
Out[45]:
species island bill_length_mm bill_depth_mm flipper_length_mm body_mass_g sex
0 Adelie Torgersen 39.1 18.7 181.0 3750.0 Male
1 Adelie Torgersen 39.5 17.4 186.0 3800.0 Female
2 Adelie Torgersen 40.3 18.0 195.0 3250.0 Female
3 Adelie Torgersen NaN NaN NaN NaN NaN
4 Adelie Torgersen 36.7 19.3 193.0 3450.0 Female
... ... ... ... ... ... ... ...
339 Gentoo Biscoe NaN NaN NaN NaN NaN
340 Gentoo Biscoe 46.8 14.3 215.0 4850.0 Female
341 Gentoo Biscoe 50.4 15.7 222.0 5750.0 Male
342 Gentoo Biscoe 45.2 14.8 212.0 5200.0 Female
343 Gentoo Biscoe 49.9 16.1 213.0 5400.0 Male

344 rows × 7 columns

In [46]:
plt.figure(figsize=(15, 5))

plt.subplot(1, 3, 1)
sns.histplot(penguins, x='bill_depth_mm')

plt.subplot(1, 3, 2)
sns.histplot(penguins, x='bill_depth_mm', bins=50)

plt.subplot(1, 3, 3)
sns.histplot(penguins, x='bill_depth_mm', binwidth=100)

plt.show()

Столбиковая диаграмма (барчарт). Используем для сравнения категорий. Тип данных - категориальные.¶

In [47]:
order=penguins["species"].value_counts()
order
Out[47]:
Adelie       152
Gentoo       124
Chinstrap     68
Name: species, dtype: int64
In [48]:
order.index
Out[48]:
Index(['Adelie', 'Gentoo', 'Chinstrap'], dtype='object')
In [49]:
penguins["species"]
Out[49]:
0      Adelie
1      Adelie
2      Adelie
3      Adelie
4      Adelie
        ...  
339    Gentoo
340    Gentoo
341    Gentoo
342    Gentoo
343    Gentoo
Name: species, Length: 344, dtype: object
In [129]:
ax = sns.countplot(x=penguins["species"], order=penguins["species"].value_counts().index)

    
# подписи значений для каждого столбика
for p in ax.patches:
    percentage ='1' + 'M'
    width, height =p.get_width(),p.get_height()
    x=p.get_x() + width - 0.5
    y=p.get_y() + height / 2 + 0.15
    ax.annotate(percentage,(x,y))

plt.title('Top-20 Countries by Population', fontsize=16);
#sns.despine() # убираем верхнюю и правую рамки
ax.tick_params(left=False)
sns.despine()
plt.show()

Барчарт с группировкой если хотим увидеть подробности внутри классов

In [51]:
sns.countplot(data=penguins, x="species", hue='sex', order=penguins["species"].value_counts().index)
plt.show()

Барчарт с накоплением. В таком случае сравнивать лучше всего получается только нижнюю часть

In [52]:
peng_m = penguins[penguins['sex'] == 'Male']
peng_f = penguins[penguins['sex'] == 'Female']
plt.bar(peng_m['island'].unique(), peng_m['island'].value_counts());
plt.bar(peng_f['island'].unique(), peng_f['island'].value_counts(), bottom = peng_m['island'].value_counts());
In [164]:
peng_pc
Out[164]:
island     species  
Biscoe     Adelie       0.269939
           Gentoo       0.730061
Dream      Adelie       0.447154
           Chinstrap    0.552846
Torgersen  Adelie       1.000000
Name: sex, dtype: float64
In [54]:
peng_pc.unstack()
Out[54]:
species Adelie Chinstrap Gentoo
island
Biscoe 0.269939 NaN 0.730061
Dream 0.447154 0.552846 NaN
Torgersen 1.000000 NaN NaN
In [184]:
# С долями (Stacked Percentage Bar Plot)
peng_pc = penguins.groupby(['island', 'species'])['sex'].count()/penguins.groupby(['island'])['sex'].count()
# pd.DataFrame(peng_pc)
ax = peng_pc.unstack().plot(kind = 'bar', stacked=True, title='Share of each species on the island', width = 0.8)
plt.legend(bbox_to_anchor=(1, 1), loc='upper left')

for p in ax.patches:
    width, height =p.get_width(), p.get_height()
    if height == 0.00:
        continue
    percentage = f'{height:.2f}'

    x = p.get_x() + width / 2 - 0.15
    y = p.get_y() + height / 2
    ax.annotate(percentage,(x,y), fontsize=15)

plt.xticks(rotation=0)
print(dir(ax))
ax.set_yticks([])
sns.despine()
plt.show()
['ArtistList', '_PROPERTIES_EXCLUDED_FROM_SET', '__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getstate__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__setstate__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', '_add_text', '_adjustable', '_agg_filter', '_alias_map', '_alpha', '_anchor', '_animated', '_aspect', '_autoscaleXon', '_autoscaleYon', '_autotitlepos', '_axes', '_axes_class', '_axes_locator', '_axis_names', '_axisbelow', '_box_aspect', '_callbacks', '_check_no_units', '_children', '_clipon', '_clippath', '_cm_set', '_colorbars', '_convert_dx', '_current_image', '_default_contains', '_deprecate_noninstance', '_facecolor', '_fill_between_x_or_y', '_frameon', '_gci', '_gen_axes_patch', '_gen_axes_spines', '_get_axis_list', '_get_axis_map', '_get_clipping_extent_bbox', '_get_lines', '_get_pan_points', '_get_patches_for_fill', '_get_view', '_gid', '_gridOn', '_in_layout', '_init_axis', '_label', '_label_outer_xaxis', '_label_outer_yaxis', '_left_title', '_make_twin_axes', '_mouseover', '_mouseover_set', '_navigate', '_navigate_mode', '_originalPosition', '_parse_scatter_color_args', '_path_effects', '_pcolor_grid_deprecation_helper', '_pcolorargs', '_picker', '_position', '_prepare_view_from_bbox', '_process_unit_info', '_projection_init', '_quiver_units', '_rasterization_zorder', '_rasterized', '_remove_legend', '_remove_method', '_request_autoscale_view', '_right_title', '_sci', '_set_alpha_for_array', '_set_artist_props', '_set_gc_clip', '_set_lim_and_transforms', '_set_position', '_set_title_offset_trans', '_set_view', '_set_view_from_bbox', '_shared_axes', '_sharex', '_sharey', '_sketch', '_snap', '_stale', '_stale_viewlims', '_sticky_edges', '_subplotspec', '_tight', '_transform', '_transformSet', '_twinned_axes', '_unit_change_handler', '_unstale_viewLim', '_update_image_limits', '_update_line_limits', '_update_patch_limits', '_update_set_signature_and_docstring', '_update_title_position', '_update_transScale', '_url', '_use_sticky_edges', '_validate_converted_limits', '_viewLim', '_visible', '_xaxis_transform', '_xmargin', '_yaxis_transform', '_ymargin', 'acorr', 'add_artist', 'add_callback', 'add_child_axes', 'add_collection', 'add_container', 'add_image', 'add_line', 'add_patch', 'add_table', 'angle_spectrum', 'annotate', 'apply_aspect', 'arrow', 'artists', 'autoscale', 'autoscale_view', 'axes', 'axhline', 'axhspan', 'axis', 'axison', 'axline', 'axvline', 'axvspan', 'bar', 'bar_label', 'barbs', 'barh', 'bbox', 'boxplot', 'broken_barh', 'bxp', 'callbacks', 'can_pan', 'can_zoom', 'change_geometry', 'child_axes', 'cla', 'clabel', 'clear', 'clipbox', 'cohere', 'collections', 'containers', 'contains', 'contains_point', 'contour', 'contourf', 'convert_xunits', 'convert_yunits', 'csd', 'dataLim', 'drag_pan', 'draw', 'draw_artist', 'end_pan', 'errorbar', 'eventplot', 'figbox', 'figure', 'fill', 'fill_between', 'fill_betweenx', 'findobj', 'fmt_xdata', 'fmt_ydata', 'format_coord', 'format_cursor_data', 'format_xdata', 'format_ydata', 'get_adjustable', 'get_agg_filter', 'get_alpha', 'get_anchor', 'get_animated', 'get_aspect', 'get_autoscale_on', 'get_autoscalex_on', 'get_autoscaley_on', 'get_axes_locator', 'get_axisbelow', 'get_box_aspect', 'get_children', 'get_clip_box', 'get_clip_on', 'get_clip_path', 'get_cursor_data', 'get_data_ratio', 'get_default_bbox_extra_artists', 'get_facecolor', 'get_fc', 'get_figure', 'get_frame_on', 'get_geometry', 'get_gid', 'get_gridspec', 'get_images', 'get_in_layout', 'get_label', 'get_legend', 'get_legend_handles_labels', 'get_lines', 'get_navigate', 'get_navigate_mode', 'get_path_effects', 'get_picker', 'get_position', 'get_rasterization_zorder', 'get_rasterized', 'get_renderer_cache', 'get_shared_x_axes', 'get_shared_y_axes', 'get_sketch_params', 'get_snap', 'get_subplotspec', 'get_tightbbox', 'get_title', 'get_transform', 'get_transformed_clip_path_and_affine', 'get_url', 'get_visible', 'get_window_extent', 'get_xaxis', 'get_xaxis_text1_transform', 'get_xaxis_text2_transform', 'get_xaxis_transform', 'get_xbound', 'get_xgridlines', 'get_xlabel', 'get_xlim', 'get_xmajorticklabels', 'get_xminorticklabels', 'get_xscale', 'get_xticklabels', 'get_xticklines', 'get_xticks', 'get_yaxis', 'get_yaxis_text1_transform', 'get_yaxis_text2_transform', 'get_yaxis_transform', 'get_ybound', 'get_ygridlines', 'get_ylabel', 'get_ylim', 'get_ymajorticklabels', 'get_yminorticklabels', 'get_yscale', 'get_yticklabels', 'get_yticklines', 'get_yticks', 'get_zorder', 'grid', 'has_data', 'have_units', 'hexbin', 'hist', 'hist2d', 'hlines', 'ignore_existing_data_limits', 'images', 'imshow', 'in_axes', 'indicate_inset', 'indicate_inset_zoom', 'inset_axes', 'invert_xaxis', 'invert_yaxis', 'is_first_col', 'is_first_row', 'is_last_col', 'is_last_row', 'is_transform_set', 'label_outer', 'legend', 'legend_', 'lines', 'locator_params', 'loglog', 'magnitude_spectrum', 'margins', 'matshow', 'minorticks_off', 'minorticks_on', 'mouseover', 'name', 'numCols', 'numRows', 'patch', 'patches', 'pchanged', 'pcolor', 'pcolorfast', 'pcolormesh', 'phase_spectrum', 'pick', 'pickable', 'pie', 'plot', 'plot_date', 'properties', 'psd', 'quiver', 'quiverkey', 'redraw_in_frame', 'relim', 'remove', 'remove_callback', 'reset_position', 'scatter', 'secondary_xaxis', 'secondary_yaxis', 'semilogx', 'semilogy', 'set', 'set_adjustable', 'set_agg_filter', 'set_alpha', 'set_anchor', 'set_animated', 'set_aspect', 'set_autoscale_on', 'set_autoscalex_on', 'set_autoscaley_on', 'set_axes_locator', 'set_axis_off', 'set_axis_on', 'set_axisbelow', 'set_box_aspect', 'set_clip_box', 'set_clip_on', 'set_clip_path', 'set_facecolor', 'set_fc', 'set_figure', 'set_frame_on', 'set_gid', 'set_in_layout', 'set_label', 'set_navigate', 'set_navigate_mode', 'set_path_effects', 'set_picker', 'set_position', 'set_prop_cycle', 'set_rasterization_zorder', 'set_rasterized', 'set_sketch_params', 'set_snap', 'set_subplotspec', 'set_title', 'set_transform', 'set_url', 'set_visible', 'set_xbound', 'set_xlabel', 'set_xlim', 'set_xmargin', 'set_xscale', 'set_xticklabels', 'set_xticks', 'set_ybound', 'set_ylabel', 'set_ylim', 'set_ymargin', 'set_yscale', 'set_yticklabels', 'set_yticks', 'set_zorder', 'sharex', 'sharey', 'specgram', 'spines', 'spy', 'stackplot', 'stairs', 'stale', 'stale_callback', 'start_pan', 'stem', 'step', 'sticky_edges', 'streamplot', 'table', 'tables', 'text', 'texts', 'tick_params', 'ticklabel_format', 'title', 'titleOffsetTrans', 'transAxes', 'transData', 'transLimits', 'transScale', 'tricontour', 'tricontourf', 'tripcolor', 'triplot', 'twinx', 'twiny', 'update', 'update_datalim', 'update_from', 'update_params', 'use_sticky_edges', 'viewLim', 'violin', 'violinplot', 'vlines', 'xaxis', 'xaxis_date', 'xaxis_inverted', 'xcorr', 'yaxis', 'yaxis_date', 'yaxis_inverted', 'zorder']

Диаграмма размаха, он же ящик с усами, он же боксплот.¶

Показывает статистические свойства по категориям - медиана, 25 и 75 квартили, минимум, максимум и выбросы.

In [56]:
sns.boxplot(data=penguins, x='species', y='body_mass_g')
plt.show()

Violin plot¶

Показывает почти всё то же, что боксплот, но ещё и форму распределения

In [57]:
sns.violinplot(data=penguins, x='species', y='body_mass_g')
plt.show()

График плотности распределения вероятности - Density plot (Kernel Density Estimation Plot)¶

Показывает форму распределения со сглаживанием

In [58]:
sns.kdeplot(penguins['bill_depth_mm'], cut=0)
#sns.distplot(penguins['bill_depth_mm'])
#sns.displot(penguins['bill_depth_mm'], kde=True, bins=30)
#sns.histplot(penguins['bill_depth_mm'], kde=True, bins=30) # kde можно подключить сразу к гистограмме
plt.show()
In [59]:
sns.kdeplot(data=penguins, x='bill_depth_mm', hue='species')
plt.show()

С гистограммой такое провернуть сложнее

In [60]:
sns.histplot(data=penguins, x='bill_depth_mm', hue='species')
plt.show()

Диаграмма рассеяния - scatterplot¶

Показывает отношение между двумя переменными

In [61]:
sns.scatterplot(data=penguins, x='body_mass_g', y='bill_length_mm', hue='species')
plt.show()
In [62]:
# чтобы увидеть линию регрессии
sns.lmplot(data=penguins, x='body_mass_g', y='bill_length_mm')
plt.show()
# sns.regplot(data=penguins, x='body_mass_g', y='bill_length_mm')
In [63]:
sns.jointplot(x="bill_length_mm",
              y="bill_depth_mm",
              data=penguins, 
              hue='species')

plt.show()

Heatmap¶

Величина показывается цветом. Удобна для изображения коэффициентов корреляции.

In [64]:
corr = penguins.corr()
corr
Out[64]:
bill_length_mm bill_depth_mm flipper_length_mm body_mass_g
bill_length_mm 1.000000 -0.235053 0.656181 0.595110
bill_depth_mm -0.235053 1.000000 -0.583851 -0.471916
flipper_length_mm 0.656181 -0.583851 1.000000 0.871202
body_mass_g 0.595110 -0.471916 0.871202 1.000000
In [65]:
sns.heatmap(corr)
plt.show()

Для более точной визуализации можно сделать шкалу от -1 до 1, выбрать расходящуюся (diverging) цветовую схему, показать точные значения в ячейках и можно убрать повторяющуюся верхнюю часть

In [67]:
mask = np.zeros_like(corr)
mask[np.triu_indices_from(mask)] = True
sns.heatmap(corr, annot=True, mask=mask, vmin=-1, vmax=1, cmap='PuOr_r', cbar=True, square=True)
plt.show()

Линейная диаграмма. Удобна для изображения изменений во времени.¶

И совсем не удобна для категорий, потому что концепция классов и соединяющие линии противоречат

In [68]:
flights = sns.load_dataset('flights')
flights
Out[68]:
year month passengers
0 1949 Jan 112
1 1949 Feb 118
2 1949 Mar 132
3 1949 Apr 129
4 1949 May 121
... ... ... ...
139 1960 Aug 606
140 1960 Sep 508
141 1960 Oct 461
142 1960 Nov 390
143 1960 Dec 432

144 rows × 3 columns

In [69]:
sns.lineplot(data=flights.groupby('month').mean(), x='month', y='passengers')
plt.show()
In [70]:
pas = flights.groupby('year')['passengers'].sum()
sns.lineplot(data=pas, x='year', y=pas.values)
plt.show()

Круговая диаграмма - pie chart.¶

Показывает доли категорий от всей выборки. Чаще всего не лучший выбор и заменяется барплотом, потому что формы сравнивать сложно.

In [71]:
plt.pie(penguins['species'].value_counts(), startangle=90, autopct='%1.f%%')
plt.legend(labels=penguins['species'].value_counts().index, loc='lower left')
plt.show()

Faceting или несколько отдельных графиков одновременно¶

In [72]:
sns.pairplot(data=penguins)
plt.show()
In [73]:
# верхняя часть повторяет нижнюю, поэтому её имеет смысл убрать
sns.pairplot(data=penguins, hue='species', corner = True)
plt.show()
In [74]:
sns.pairplot(data=penguins, hue='species', kind='hist', corner = True)
plt.show()
In [75]:
                        # по какому столбцу бьем, разбиение
g = sns.FacetGrid(penguins, col="sex", hue="species")
g.map(sns.scatterplot, "bill_length_mm", "flipper_length_mm")
# plt.legend()
plt.show()
In [76]:
g = sns.FacetGrid(penguins, col="species")
g.map(sns.histplot, "bill_length_mm")
# g.map(sns.histplot, "flipper_length_mm")
plt.show()

Subplots

In [77]:
fig, axes = plt.subplots(1, 4, figsize=(17, 4))
In [78]:
fig, axes = plt.subplots(1, 4, figsize=(19, 4))
sns.violinplot(ax=axes[0], data=penguins, x='species', y='flipper_length_mm')
sns.violinplot(ax=axes[1], data=penguins, x='species', y='bill_length_mm')
sns.barplot(ax=axes[2], data=penguins, x='species', y='bill_depth_mm', estimator=np.median)
sns.boxplot(ax=axes[3], data=penguins, x='species', y='body_mass_g')
fig.suptitle('Distribution of characteristics by species', fontsize=16);
# plt.subplots_adjust(right=1.1)
plt.savefig('./jgpasgjas.jpg')
In [79]:
fig, axes = plt.subplots(4, 3, figsize=(18, 18), sharey = True)
ro = 0
co = 0
for i in penguins.columns[2:6]:
    for j in penguins['species'].unique():
        sns.histplot(data=penguins[penguins['species'] == j][i], ax=axes[ro, co], kde=True)
        if ro==0:
            axes[ro, co].set_title(j, pad=2, fontsize=20)
        co+=1
        if co>=3:
            co=0
            
    ro+=1

Правила хорошего тона, общепринятые практики, нюансы¶

In [98]:
colors = {'Adelie': "#FC3F1D", 'Chinstrap': "#7cec84", 'Gentoo': "#1E1E1E"} #1E1E1E
order = penguins["species"].value_counts().index
order
Out[98]:
Index(['Adelie', 'Gentoo', 'Chinstrap'], dtype='object')
In [99]:
colors
Out[99]:
{'Adelie': '#FC3F1D', 'Chinstrap': '#7cec84', 'Gentoo': '#1E1E1E'}
In [100]:
sns.countplot(x=penguins["species"], order=order, palette=colors)
plt.show()

Сортировка¶

In [34]:
sns.countplot(x=penguins["species"], order=order, palette=colors)
plt.show()

Последовательность в цветах категорий¶

нехорошо, если на разных графиках в одном проекте одни и те же классы показывают разными цветами/формами. Классы должны оставаться узнаваемыми от графика к графику

In [35]:
vorder = penguins.groupby('species').median()['body_mass_g'].sort_values(ascending=False).index
vorder
Out[35]:
Index(['Gentoo', 'Adelie', 'Chinstrap'], dtype='object', name='species')
In [36]:
sns.violinplot(data=penguins, x='species', y='body_mass_g', palette=colors, order=vorder)
plt.show()
In [37]:
sns.kdeplot(data=penguins, x='body_mass_g', hue='species', palette=colors)
plt.show()
In [38]:
sns.histplot(data=penguins, x='body_mass_g', hue='species', palette=colors)
plt.show()
In [39]:
sns.pairplot(data=penguins, hue='species', corner=True, palette=colors)
plt.show()

Шкала времени идёт по оси Х¶

In [40]:
# Странненько. Пугает.
g = sns.lineplot(data=flights.groupby('year')['passengers'].sum(), 
             y='year', 
             x=flights.groupby('year')['passengers'].sum().values)

xlabels = ['{:d}'.format(int(x)) + 'K' for x in g.get_xticks()/1000]
g.set_xticklabels(xlabels)
sns.despine()

Добавить элементы графика¶

Названия, подписи осей, легенда, источник данных, пояснения

Убрать лишние детали ("chart junk" или "графический мусор") - сетки, повторяющиеся детали, рамки, палочки¶

sns.despine()

10000, 20000, 50000, 550000 удобно записать как 10K, 20K, 50K, 550K

Повторяющиеся оси на нескольких типовых графиках можно показать один раз

3D эффекты, тени, градиенты в заливке графика и фона, частые или яркие линии сетки создают лишний шум


Способы добавления измерений¶

  • Форма
  • Размер
  • Прозрачность
  • Цвет

Работа с цветом¶

  • Цвет - один из способов добавления измерений наравне с формой, размером, прозрачностью
  • Работает и с категориями, и с непрерывными значениями
  • Выделяет элементы

Цветовые схемы¶

  • Дискретные - для разделения на категории. Классы должны иметь хорошо различимые цвета
In [36]:
sns.color_palette("bright", 9)
Out[36]:
  • Последовательные (continuous)
In [37]:
sns.color_palette("mako", 9)
Out[37]:
  • Расходящиеся (diverging) - когда есть смысловая середина, отклонения от которой надо подчеркнуть
In [38]:
sns.color_palette("vlag", 9)
Out[38]:

Радужная цветовая схема обладает смешанными свойствами - одновременно категориальными и непрерывными

Непостоянная яркость - желтый и голубой утягивают внимание

Даже при использовании для категорий переходы резкие

Для более справедливого использования разработаны специальные равномерные схемы - viridis, inferno, plasma ...

Тем не менее, иногда она лучше передаёт детали

RainbowUrl

In [39]:
sns.color_palette("rainbow", 9)
Out[39]:
In [40]:
sns.color_palette("viridis", 9)
Out[40]:
In [41]:
sns.color_palette("plasma", 9)
Out[41]:
In [42]:
sns.color_palette("magma", 9)
Out[42]:

Если мы пытаемся нарисовать слишком много категорий - больше, чем в текущей палитре - seaborn присваивает им круговую схему

In [43]:
sns.color_palette("husl", 15)
Out[43]:

Ограниченное количество категорий, разделённых цветом¶

Идеально - около 5, но больше 7 уже нехорошо. Можно группировать категории, чтобы их стало поменьше.

In [102]:
# https://www.kaggle.com/fernandol/countries-of-the-world?select=countries+of+the+world.csv
w = pd.read_csv('countries of the world.csv')
w
Out[102]:
Country Region Population Area (sq. mi.) Pop. Density (per sq. mi.) Coastline (coast/area ratio) Net migration Infant mortality (per 1000 births) GDP ($ per capita) Literacy (%) Phones (per 1000) Arable (%) Crops (%) Other (%) Climate Birthrate Deathrate Agriculture Industry Service
0 Afghanistan ASIA (EX. NEAR EAST) 31056997 647500 48,0 0,00 23,06 163,07 700.0 36,0 3,2 12,13 0,22 87,65 1 46,6 20,34 0,38 0,24 0,38
1 Albania EASTERN EUROPE 3581655 28748 124,6 1,26 -4,93 21,52 4500.0 86,5 71,2 21,09 4,42 74,49 3 15,11 5,22 0,232 0,188 0,579
2 Algeria NORTHERN AFRICA 32930091 2381740 13,8 0,04 -0,39 31 6000.0 70,0 78,1 3,22 0,25 96,53 1 17,14 4,61 0,101 0,6 0,298
3 American Samoa OCEANIA 57794 199 290,4 58,29 -20,71 9,27 8000.0 97,0 259,5 10 15 75 2 22,46 3,27 NaN NaN NaN
4 Andorra WESTERN EUROPE 71201 468 152,1 0,00 6,6 4,05 19000.0 100,0 497,2 2,22 0 97,78 3 8,71 6,25 NaN NaN NaN
... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ...
222 West Bank NEAR EAST 2460492 5860 419,9 0,00 2,98 19,62 800.0 NaN 145,2 16,9 18,97 64,13 3 31,67 3,92 0,09 0,28 0,63
223 Western Sahara NORTHERN AFRICA 273008 266000 1,0 0,42 NaN NaN NaN NaN NaN 0,02 0 99,98 1 NaN NaN NaN NaN 0,4
224 Yemen NEAR EAST 21456188 527970 40,6 0,36 0 61,5 800.0 50,2 37,2 2,78 0,24 96,98 1 42,89 8,3 0,135 0,472 0,393
225 Zambia SUB-SAHARAN AFRICA 11502010 752614 15,3 0,00 0 88,29 800.0 80,6 8,2 7,08 0,03 92,9 2 41 19,93 0,22 0,29 0,489
226 Zimbabwe SUB-SAHARAN AFRICA 12236805 390580 31,3 0,00 0 67,69 1900.0 90,7 26,8 8,32 0,34 91,34 2 28,01 21,84 0,179 0,243 0,579

227 rows × 20 columns

In [103]:
# w.dtypes
w_num = w.copy()
for i in w_num.columns[4:8].tolist() +  w_num.columns[9:14].tolist() + w_num.columns[15:20].tolist():
    print(i)
    w_num[i] = w_num[i].str.replace(',', '.')
    w_num[i] = w_num[i].astype(float)
Pop. Density (per sq. mi.)
Coastline (coast/area ratio)
Net migration
Infant mortality (per 1000 births)
Literacy (%)
Phones (per 1000)
Arable (%)
Crops (%)
Other (%)
Birthrate
Deathrate
Agriculture
Industry
Service
In [104]:
# первая попытка
plt.figure(figsize=(10, 30))
ax = sns.barplot(data=w_num, 
                y='Country', x='Population', 
                order=w_num['Country'], # по алфавиту
                orient='h', hue='Region', dodge=False)
In [105]:
plt.figure(figsize=(10, 30))

ax = sns.barplot(data=w_num.sort_values(by='Population', ascending=False), 
                y='Country', x='Population', 
                order=w_num.sort_values(by='Population', ascending=False)['Country'], 
                orient='h', hue='Region', dodge=False)
In [106]:
# убрать пробелы справа
w_num['Region'] = w_num['Region'].apply(lambda x: x.rstrip())
# объединить регионы
w_num['Region_common'] = w_num['Region'].str.replace('EASTERN EUROPE', 'EUROPE', regex=False)
w_num['Region_common'] = w_num['Region_common'].str.replace('C.W. OF IND. STATES', 'EUROPE', regex=False)
w_num['Region_common'] = w_num['Region_common'].str.replace('WESTERN EUROPE', 'EUROPE', regex=False)
w_num['Region_common'] = w_num['Region_common'].str.replace('BALTICS', 'EUROPE', regex=False)
w_num['Region_common'] = w_num['Region_common'].str.replace('NORTHERN AFRICA', 'AFRICA', regex=False)
w_num['Region_common'] = w_num['Region_common'].str.replace('SUB-SAHARAN AFRICA', 'AFRICA', regex=False)
# w_num['Region'] = w_num['Region'].str.replace('NEAR EAST', 'ASIA', regex=False)
w_num['Region_common'] = w_num['Region_common'].str.replace('ASIA (EX. ASIA)', 'ASIA', regex=False)
In [107]:
w_num['Region_common'].unique()
Out[107]:
array(['ASIA (EX. NEAR EAST)', 'EUROPE', 'AFRICA', 'OCEANIA',
       'LATIN AMER. & CARIB', 'NEAR EAST', 'NORTHERN AMERICA'],
      dtype=object)
In [108]:
w_popdens = w_num[w_num['Pop. Density (per sq. mi.)'] >= 300].sort_values(by='Pop. Density (per sq. mi.)', ascending=False)
# w_pop = w[w['Population'] >= 31000000].sort_values(by='Population', ascending=False)
w_pop = w_num.sort_values(by='Population', ascending=False)[0:21]
w_pop
Out[108]:
Country Region Population Area (sq. mi.) Pop. Density (per sq. mi.) Coastline (coast/area ratio) Net migration Infant mortality (per 1000 births) GDP ($ per capita) Literacy (%) ... Arable (%) Crops (%) Other (%) Climate Birthrate Deathrate Agriculture Industry Service Region_common
42 China ASIA (EX. NEAR EAST) 1313973713 9596960 136.9 0.15 -0.40 24.18 5000.0 90.9 ... 15.40 1.25 83.35 1,5 13.25 6.97 0.125 0.473 0.403 ASIA (EX. NEAR EAST)
94 India ASIA (EX. NEAR EAST) 1095351995 3287590 333.2 0.21 -0.07 56.29 2900.0 59.5 ... 54.40 2.74 42.86 2,5 22.01 8.18 0.186 0.276 0.538 ASIA (EX. NEAR EAST)
214 United States NORTHERN AMERICA 298444215 9631420 31.0 0.21 3.41 6.50 37800.0 97.0 ... 19.13 0.22 80.65 3 14.14 8.26 0.010 0.204 0.787 NORTHERN AMERICA
95 Indonesia ASIA (EX. NEAR EAST) 245452739 1919440 127.9 2.85 0.00 35.60 3200.0 87.9 ... 11.32 7.23 81.45 2 20.34 6.25 0.134 0.458 0.408 ASIA (EX. NEAR EAST)
27 Brazil LATIN AMER. & CARIB 188078227 8511965 22.1 0.09 -0.03 29.61 7600.0 86.4 ... 6.96 0.90 92.15 2 16.56 6.17 0.084 0.400 0.516 LATIN AMER. & CARIB
156 Pakistan ASIA (EX. NEAR EAST) 165803560 803940 206.2 0.13 -2.77 72.44 2100.0 45.7 ... 27.87 0.87 71.26 1 29.74 8.23 0.216 0.251 0.533 ASIA (EX. NEAR EAST)
16 Bangladesh ASIA (EX. NEAR EAST) 147365352 144000 1023.4 0.40 -0.71 62.60 1900.0 43.1 ... 62.11 3.07 34.82 2 29.80 8.27 0.199 0.198 0.603 ASIA (EX. NEAR EAST)
169 Russia C.W. OF IND. STATES 142893540 17075200 8.4 0.22 1.02 15.39 8900.0 99.6 ... 7.33 0.11 92.56 NaN 9.95 14.65 0.054 0.371 0.575 EUROPE
152 Nigeria SUB-SAHARAN AFRICA 131859731 923768 142.7 0.09 0.26 98.80 900.0 68.0 ... 31.29 2.96 65.75 1,5 40.43 16.94 0.269 0.487 0.244 AFRICA
103 Japan ASIA (EX. NEAR EAST) 127463611 377835 337.4 7.87 0.00 3.26 28200.0 99.0 ... 12.19 0.96 86.85 3 9.37 9.16 0.017 0.258 0.725 ASIA (EX. NEAR EAST)
135 Mexico LATIN AMER. & CARIB 107449525 1972550 54.5 0.47 -4.87 20.91 9000.0 92.2 ... 12.99 1.31 85.70 1,5 20.69 4.74 0.038 0.259 0.702 LATIN AMER. & CARIB
162 Philippines ASIA (EX. NEAR EAST) 89468677 300000 298.2 12.10 -1.50 23.51 4600.0 92.6 ... 18.95 16.77 64.28 2 24.89 5.41 0.144 0.326 0.530 ASIA (EX. NEAR EAST)
219 Vietnam ASIA (EX. NEAR EAST) 84402966 329560 256.1 1.05 -0.45 25.95 2500.0 90.3 ... 19.97 5.95 74.08 2 16.86 6.22 0.209 0.410 0.381 ASIA (EX. NEAR EAST)
76 Germany WESTERN EUROPE 82422299 357021 230.9 0.67 2.18 4.16 27600.0 99.0 ... 33.85 0.59 65.56 3 8.25 10.62 0.009 0.296 0.695 EUROPE
60 Egypt NORTHERN AFRICA 78887007 1001450 78.8 0.24 -0.22 32.59 4000.0 57.7 ... 2.87 0.48 96.65 1 22.94 5.23 0.149 0.357 0.493 AFRICA
65 Ethiopia SUB-SAHARAN AFRICA 74777981 1127127 66.3 0.00 0.00 95.32 700.0 42.7 ... 10.71 0.75 88.54 2 37.98 14.86 0.475 0.099 0.426 AFRICA
206 Turkey NEAR EAST 70413958 780580 90.2 0.92 0.00 41.04 6700.0 86.5 ... 30.93 3.31 65.76 3 16.62 5.97 0.117 0.298 0.585 NEAR EAST
96 Iran ASIA (EX. NEAR EAST) 68688433 1648000 41.7 0.15 -0.84 41.58 7000.0 79.4 ... 8.72 1.39 89.89 1 17.00 5.55 0.116 0.424 0.460 ASIA (EX. NEAR EAST)
201 Thailand ASIA (EX. NEAR EAST) 64631595 514000 125.7 0.63 0.00 20.48 7400.0 92.6 ... 29.36 6.46 64.18 2 13.87 7.04 0.099 0.441 0.460 ASIA (EX. NEAR EAST)
45 Congo, Dem. Rep. SUB-SAHARAN AFRICA 62660551 2345410 26.7 0.00 0.00 94.69 700.0 65.5 ... 2.96 0.52 96.52 2 43.69 13.27 0.550 0.110 0.340 AFRICA
69 France WESTERN EUROPE 60876136 547030 111.3 0.63 0.66 4.26 27600.0 99.0 ... 33.53 2.07 64.40 4 11.99 9.14 0.022 0.214 0.764 EUROPE

21 rows × 21 columns

In [109]:
muted = sns.color_palette("muted", 20)
muted
Out[109]:
In [110]:
colors = {'EUROPE': muted[0], 
          'AFRICA': 'yellowgreen', 
          'OCEANIA': muted[9], 
          'ASIA (EX. NEAR EAST)': muted[3], 
          'LATIN AMER. & CARIB': 'tab:green',
          'NEAR EAST': muted[8],
          'NORTHERN AMERICA': muted[5]
         }
In [111]:
plt.figure(figsize=(10, 10))
ax = sns.barplot(data=w_pop, 
                y='Country', x='Population', 
                order=w_pop['Country'], 
                orient='h', 
                hue='Region_common', 
                dodge=False, 
                palette=colors)
xlabels = ['{:,.0f}'.format(x/1000000) + 'M' for x in ax.get_xticks()]
ax.set_xticklabels(xlabels);

# подписи значений для каждого столбика
for p in ax.patches:
    percentage ='{:,.0f}'.format(p.get_width()/1000000) + 'M'
    width, height =p.get_width(),p.get_height()
    x=p.get_x()+width+5000000
    y=p.get_y()+height/2+0.15
    ax.annotate(percentage,(x,y))

plt.title('Top-20 Countries by Population', fontsize=16);
sns.despine() # убираем верхнюю и правую рамки
ax.tick_params(left=False)
In [112]:
plt.figure(figsize=(10, 10))
sns.barplot(data=w_num, 
            x='Deathrate', 
            y='Country', 
            order=w_num.sort_values(by='Deathrate', ascending=False)['Country'][0:51],
            hue='Region_common',
            dodge=False,
            palette=colors)
plt.title('World countries by deathrate', fontsize=16);

Цветовые обычаи¶

Выбирая цвета, хорошо соблюдать принятые нормы и не нарушать привычные ассоциации, например

  • темнее значит больше
  • зелёное значит хорошо, красное значит плохо
  • холодные цвета - мужчины, теплые цвета - женщины (спорно, но интуитивно)

Чтобы сделать акцент, можно сделать остальное серым или бледным¶

In [113]:
colors_accent = ['lightgray'] * 7 + ['tab:red'] + ['lightgray'] * 13
In [115]:
plt.figure(figsize=(10, 10))
ax = sns.barplot(data=w_pop, 
                y='Country', x='Population', 
                order=w_pop['Country'], 
                orient='h', dodge=False, palette=colors_accent)
xlabels = ['{:,.0f}'.format(x/1000000) + 'M' for x in ax.get_xticks()]
ax.set_xticklabels(xlabels);

# подписи значений для каждого столбика
for p in ax.patches:
    percentage ='{:,.0f}'.format(p.get_width()/1000000) + 'M'
    width, height =p.get_width(),p.get_height()
    x=p.get_x()+width+5000000
    y=p.get_y()+height/2+0.15
    ax.annotate(percentage,(x,y))

plt.title('Russia among Top-20 Countries by Population', fontsize=16);
sns.despine() # убираем верхнюю и правую рамки
ax.tick_params(left=False)

Цветовые схемы для дальтоников¶

https://colorbrewer2.org/#type=sequential&scheme=BuGn&n=3 ColorBrewer

Здесь же можно смотреть коды цветов из многих стандартных палитр


Как можно случайно внести смуту¶

  • Шкала барплота, начинающаяся не с нуля
  • Добавить плоскому графику объем и перспективу
  • Сделать что-нибудь, чего никто не ожидает: перевернуть шкалу, показать оси сверху и справа,
  • Выбрать неподходящую цветовую схему

...

In [62]:
# обманные манёвры презента
ab = pd.DataFrame({'col1': ['a', 'b'], 'col2': [113, 110]})
sns.barplot(data=ab, x='col1', y='col2')

ab = pd.DataFrame({'col1': ['a', 'b'], 'col2': [113, 110]})
sns.barplot(data=ab, x='col1', y='col2')
plt.ylim(100, 115)

# внезапные манёвры
sns.regplot(data=w_num, y='Literacy (%)', x=np.log10(w_num['Population']))
plt.tick_params(right=True, top=True, bottom=False, left=False, labelleft=False, labelbottom=False, labelright=True, labeltop=True)
sns.despine(right=False, top=False, bottom=True, left=True)

ab = pd.DataFrame({'col1': ['% positive', '% negative', '% neutral'], 'col2': [11, 8, 5]})
sns.barplot(data=ab, x='col1', y='col2', palette='PuBu')
Out[62]:
<AxesSubplot: xlabel='col1', ylabel='col2'>

Как показать большое количество точек¶

In [63]:
x1 = np.random.normal(0, 0.5, 10000) 
d1 = x1**2 + x1 + np.random.normal(0, 0.5, 10000) + 2
x2 = np.random.normal(0, 0.1, 10000) + 1
d2 = x2**2 + x2 + np.random.normal(0, 0.3, 10000) + 1
x = np.append(x1,x2)
d = np.append(d1,d2)

sns.scatterplot(x, d)
plt.show()
In [64]:
# 1. уменьшить размер точек и увеличить прозрачность
sns.scatterplot(x, d, alpha=0.3, s=0.6, ) 
plt.show()
In [65]:
# 2. Разновидность тепловой карты
plt.hist2d(x, d, bins=(100, 100), cmap = 'Blues');
plt.colorbar()
plt.show()
In [66]:
sns.jointplot(x=x, y=d, kind='hex', height=6)
plt.show()
In [67]:
xd = pd.concat([pd.Series(x), pd.Series(d)], axis=1)
sns.pairplot(xd, kind="hist", corner=True, diag_kind='kde', height=4)
plt.show()
In [68]:
# 3. Выборка
df = pd.DataFrame({'x':x, 'd':d}).sample(1000)
sns.scatterplot(data=df, x='x', y='d', alpha=0.3, s=4)
plt.show()

Как визуализировать вместе с выбросами¶

Выбросы утягивают на себя шкалы и скрывают остальные значения

In [69]:
# Показать на одном графике общее распределение...
x = np.append(np.random.normal(1, 0.5, 20), [20, 12])
y = np.append(np.random.normal(1, 0.5, 20), [20, 18])
sns.scatterplot(x, y)
plt.show()
In [70]:
# ...и на втором часть без выбросов
xy = pd.DataFrame(columns=['x', 'y'])
xy['x'] = x
xy['y'] = y

def reject_outliers(data, m=2):
    return data[abs(data - np.mean(data)) < m * np.std(data)]

sns.scatterplot(x=reject_outliers(xy).x, y=reject_outliers(xy).y)
plt.show()
In [71]:
# Преобразовать шкалу
ax = sns.scatterplot(x, y)
ax.set_xscale('log')
ax.set_yscale('log')
In [72]:
# ~~Подсечь шкалу
In [73]:
# qqplot - для проверки нормальности распределения. Линия из точек хорошо совпадает с диагональю значит, распределение нормальное
from scipy import stats
stats.probplot(w_num['Population'], plot=sns.mpl.pyplot)
stats.probplot(penguins[penguins['species'] == 'Adelie']['flipper_length_mm'], plot=sns.mpl.pyplot)
plt.show()

Другие библиотеки для визуализации¶

In [74]:
# Plotly - интерактивные графики
import plotly.express as px
import plotly.graph_objects as go
In [75]:
w_num['GDP ($ per capita)'][223] = 0
colors_px = {'EUROPE': 'steelblue', 
          'AFRICA': 'yellowgreen', 
          'OCEANIA': 'skyblue', 
          'ASIA (EX. NEAR EAST)': 'red', 
          'LATIN AMER. & CARIB': 'green',
          'NEAR EAST': 'orange',
          'NORTHERN AMERICA': 'brown'
         }
In [76]:
fig = px.scatter(w_num, 
                 y=np.log10(w_num['Population']), 
                 x=np.log10(w_num['Area (sq. mi.)']), 
                 size='GDP ($ per capita)', 
                 color='Region_common',
                 hover_data=['Country', 'Population', 'Area (sq. mi.)', 'GDP ($ per capita)'],
                 # color_discrete_sequence=['red', 'steelblue', 'yellowgreen', 'skyblue', 'green', 'orange', 'brown'],
                 color_discrete_map=colors_px,
                 title="World Countries Population vs Area"
                )

fig.update_layout(
    title_y=0.86,
    title_x=0.08,
    xaxis_title="Log10 Area (sq. mi)",
    yaxis_title="Log10 Population",
    legend_title="Region",
#     font=dict(
#         size=13,
#         color="darkblue"
#     )
)

fig.update_layout(annotations=[
       go.layout.Annotation(
            showarrow=False,
            text='Marker size represents GDP in $ per capita',
            x=1.3,
            y=9.2,
        )])

fig.show()
In [77]:
fig = px.bar(w_num, y="Region_common", x="Population")
fig.update_layout(yaxis={'categoryorder':'total ascending'})
fig.show()
In [78]:
# treemap показывает иерархичные структуры и пропорции в составе групп
w_num["world"] = "world" # создаём общую группу для всех элементов
fig = px.treemap(w_num, 
                path=['world', 'Region_common', 'Country'], 
                values='Population',
                color='GDP ($ per capita)', 
                hover_data=['Country', 'GDP ($ per capita)', 'Population'],
                color_continuous_scale='OrRd',
                title='World Countries Population')

fig.show()
In [79]:
#pip install altair
In [80]:
# altair можно делать и статичные, и интерактивные визуализации
import altair as alt
In [81]:
alt.Chart(w_num).transform_density(
    'Deathrate',
    as_=['Deathrate', 'density'],
).mark_area(
    color='red',opacity=0.3
).encode(
    x="Deathrate:Q",
    y='density:Q',
)
Out[81]:
In [82]:
alt.Chart(flights).mark_rect().encode(
    x='year:O',
    y=alt.Y('month', sort=['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']),
    color='sum(passengers):Q',
    tooltip=['passengers']
).properties(
    width=300,
    height=300
).interactive()
Out[82]:
In [83]:
alt.Chart(flights.groupby('month').mean().reset_index()).mark_line(
    color='green'
).encode(
    x=alt.X('month', sort=['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']),
    y=alt.Y('passengers', scale=alt.Scale(zero=False)),
    tooltip=['passengers'],
).properties(
    width=500,
    height=300
).interactive()
Out[83]:
In [84]:
#pip install plotnine
In [85]:
# ggplot - пакет для визуализации на R, в питоне пакет называется plotnine
from plotnine import *
In [86]:
w_regcount = w_num['Region'].value_counts().reset_index()
w_regcount.columns = ['region', 'count']
In [87]:
ggorder = w_num['Region'].value_counts().index.tolist()[::-1]
ggplot(data=w_regcount) +\
geom_bar(aes(x='region', y='count'), stat='identity', fill='purple') +\
coord_flip() + scale_x_discrete(limits=ggorder) +\
ggtitle("Count of Countries per Region")
Out[87]:
<ggplot: (8789827193561)>
In [88]:
birth = pd.DataFrame(w_num.groupby('Region').mean('Birthrate').sort_values(by='Birthrate', ascending=False)['Birthrate']).reset_index()
b_order = w_num.groupby('Region').mean('Birthrate').sort_values(by='Birthrate', ascending=False).index.tolist()[::-1]

ggplot(birth, aes(x='Region', y='Birthrate')) +\
geom_bar(stat="identity") +\
coord_flip() + scale_x_discrete(limits=b_order) +\
ggtitle("Mean Birthrate per Region")
Out[88]:
<ggplot: (8789827874392)>
In [89]:
ggplot(data=w_num) + geom_histogram(aes(x='Birthrate'), fill='steelblue', color='black', bins=50)
Out[89]:
<ggplot: (8789826221367)>
In [90]:
ggplot(data=w_num) +\
geom_point(aes(x='GDP ($ per capita)', y='Birthrate', color='Region_common')) +\
scale_color_manual(values=colors_px) 
# + scale_x_log10()
Out[90]:
<ggplot: (8789826269492)>

Практика¶

In [91]:
w_num
Out[91]:
Country Region Population Area (sq. mi.) Pop. Density (per sq. mi.) Coastline (coast/area ratio) Net migration Infant mortality (per 1000 births) GDP ($ per capita) Literacy (%) ... Crops (%) Other (%) Climate Birthrate Deathrate Agriculture Industry Service Region_common world
0 Afghanistan ASIA (EX. NEAR EAST) 31056997 647500 48.0 0.00 23.06 163.07 700.0 36.0 ... 0.22 87.65 1 46.60 20.34 0.380 0.240 0.380 ASIA (EX. NEAR EAST) world
1 Albania EASTERN EUROPE 3581655 28748 124.6 1.26 -4.93 21.52 4500.0 86.5 ... 4.42 74.49 3 15.11 5.22 0.232 0.188 0.579 EUROPE world
2 Algeria NORTHERN AFRICA 32930091 2381740 13.8 0.04 -0.39 31.00 6000.0 70.0 ... 0.25 96.53 1 17.14 4.61 0.101 0.600 0.298 AFRICA world
3 American Samoa OCEANIA 57794 199 290.4 58.29 -20.71 9.27 8000.0 97.0 ... 15.00 75.00 2 22.46 3.27 NaN NaN NaN OCEANIA world
4 Andorra WESTERN EUROPE 71201 468 152.1 0.00 6.60 4.05 19000.0 100.0 ... 0.00 97.78 3 8.71 6.25 NaN NaN NaN EUROPE world
... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ...
222 West Bank NEAR EAST 2460492 5860 419.9 0.00 2.98 19.62 800.0 NaN ... 18.97 64.13 3 31.67 3.92 0.090 0.280 0.630 NEAR EAST world
223 Western Sahara NORTHERN AFRICA 273008 266000 1.0 0.42 NaN NaN 0.0 NaN ... 0.00 99.98 1 NaN NaN NaN NaN 0.400 AFRICA world
224 Yemen NEAR EAST 21456188 527970 40.6 0.36 0.00 61.50 800.0 50.2 ... 0.24 96.98 1 42.89 8.30 0.135 0.472 0.393 NEAR EAST world
225 Zambia SUB-SAHARAN AFRICA 11502010 752614 15.3 0.00 0.00 88.29 800.0 80.6 ... 0.03 92.90 2 41.00 19.93 0.220 0.290 0.489 AFRICA world
226 Zimbabwe SUB-SAHARAN AFRICA 12236805 390580 31.3 0.00 0.00 67.69 1900.0 90.7 ... 0.34 91.34 2 28.01 21.84 0.179 0.243 0.579 AFRICA world

227 rows × 22 columns

In [92]:
#  Как связаны переменные Industry и Birthrate?
sns.regplot(data=w_num, x='Industry', y='Birthrate')
Out[92]:
<AxesSubplot: xlabel='Industry', ylabel='Birthrate'>
In [93]:
# Как распределены площади стран мира?
fig = plt.figure(figsize=(7, 5))
sns.histplot(data=w_num, x=np.log10(w_num['Area (sq. mi.)']), bins=30, kde=True)
# plt.xticks(ticks=np.arange(8), labels=[10**i for i in range(8)])
plt.xlabel('Log10 Area')
Out[93]:
Text(0.5, 0, 'Log10 Area')
In [94]:
# как скоррелированы переменные датасета?
w_corr = w_num.corr()

fig = plt.figure(figsize=(12, 10))
mask = np.zeros_like(w_corr)
mask[np.triu_indices_from(mask)] = True
sns.heatmap(w_corr, annot=True, mask=mask, vmin=-1, vmax=1, cmap='PuOr_r', cbar=True, square=True, fmt='.2f')
Out[94]:
<AxesSubplot: >
In [95]:
# w_log = w_num.copy()
# w_log = w_log.drop(['Infant mortality (per 1000 births)', 'Phones (per 1000)'], axis=1)
# for i in w_log.columns[2:6]:
#     w_log[i] = np.log10(w_log[i])

# w_log.isna().sum()
# w_log.replace([np.inf, -np.inf], np.nan, inplace=True)
# sns.pairplot(data=w_log.dropna(), corner=True, kind="reg")
#  # plot_kws={'line_kws':{'color':'red'}}
In [96]:
plt.figure(figsize=(10, 10))
sns.scatterplot(data=w_num, 
            y=np.log10(w_num['Population']), 
            x=np.log10(w_num['Area (sq. mi.)']),
            hue='Region_common',
            size='GDP ($ per capita)',
            sizes=(10, 500),
            alpha=0.8,
            palette=colors)
plt.legend(bbox_to_anchor=(1.01, 1), borderaxespad=0, labelspacing=1.2, borderpad=1)
plt.ylabel('log$_{10}$(Population)')
plt.xlabel('log$_{10}$(Area)')
plt.title('Population vs Area', size=16, pad=20);
In [97]:
w_llp = w_num[['Country', 'Deathrate', 'Birthrate', 'Region', 'Region_common']]
w_llp = w_llp.dropna()
w_llp['brth_dth_diff'] = w_llp['Deathrate'] - w_llp['Birthrate'] # разница между рождаемостью и смертностью
w_llp['diff_color'] = ['tab:green' if x < 0 else 'tab:red' for x in w_llp['brth_dth_diff']] # присваиваем значения в виде цвета (можно было 0 и 1)
w_llp = w_llp.sort_values('Birthrate', ascending=False)
w_llp
Out[97]:
Country Deathrate Birthrate Region Region_common brth_dth_diff diff_color
151 Niger 20.91 50.73 SUB-SAHARAN AFRICA AFRICA -29.82 tab:green
128 Mali 16.89 49.82 SUB-SAHARAN AFRICA AFRICA -32.93 tab:green
210 Uganda 12.24 47.35 SUB-SAHARAN AFRICA AFRICA -35.11 tab:green
0 Afghanistan 20.34 46.60 ASIA (EX. NEAR EAST) ASIA (EX. NEAR EAST) -26.26 tab:green
183 Sierra Leone 23.03 45.76 SUB-SAHARAN AFRICA AFRICA -22.73 tab:green
... ... ... ... ... ... ... ...
101 Italy 10.40 8.72 WESTERN EUROPE EUROPE 1.68 tab:red
4 Andorra 6.25 8.71 WESTERN EUROPE EUROPE -2.46 tab:green
122 Macau 4.47 8.48 ASIA (EX. NEAR EAST) ASIA (EX. NEAR EAST) -4.01 tab:green
76 Germany 10.62 8.25 WESTERN EUROPE EUROPE 2.37 tab:red
91 Hong Kong 6.29 7.29 ASIA (EX. NEAR EAST) ASIA (EX. NEAR EAST) -1.00 tab:green

223 rows × 7 columns

In [98]:
plt.figure(figsize=(10, 40))
plt.hlines(data=w_llp, y='Country', xmin='Deathrate', xmax='Birthrate', colors='diff_color') # сначала строим линии
plt.margins(y=0.005) # убираем белые поля
# plt.yticks(np.arange(0, len(w_llp['Country']), 2.0));

plt.scatter(data=w_llp, y='Country', x='Deathrate', color='tab:red', alpha=1, label='Deathrate', s=15)
plt.scatter(data=w_llp, y='Country', x='Birthrate', color='tab:green', alpha=1, label='Birthrate', s=15)
plt.legend()
plt.grid(linestyle='--', alpha=0.5)
plt.title('Difference between birthrate and deathrate\n in countries of the world', size=16, pad=20);
In [99]:
# прошлый график слишком неудобный, поэтому имеет смысл поделить его на части, например, возьмём Европу
w_llp_eu = w_llp[w_llp['Region_common'] == 'EUROPE']
w_llp_eu = w_llp_eu.sort_values('Deathrate', ascending=False)
In [100]:
plt.figure(figsize=(10, 15))
plt.hlines(data=w_llp_eu, y='Country', xmin='Deathrate', xmax='Birthrate', colors='diff_color') # сначала строим линии
plt.margins(y=0.005)

plt.scatter(data=w_llp_eu, y='Country', x='Deathrate', color='tab:red', alpha=1, label='Deathrate', s=20) # добавляем точки
plt.scatter(data=w_llp_eu, y='Country', x='Birthrate', color='tab:green', alpha=1, label='Birthrate', s=20)
plt.legend() # вместо классической легенды можно было бы указать цвета в названии графика
plt.grid(linestyle='--', alpha=0.5)
plt.title('Difference between birthrate and deathrate\n in countries of Europe and former USSR', size=16, pad=20);
sns.despine(left=True, bottom=True) # убираем все границы
plt.tick_params(left=False, bottom=False) # убираем палочки слева и снизу
In [ ]:
 
In [ ]: